# -*- coding: utf-8 -*-
import serial
import time
import crcmod.predefined
import socket

class ESP32CommandClient:
    def __init__(self, comm_type='serial', port='COM8', baudrate=115200, 
                 host='192.168.0.66', port_tcp=10123, timeout=1):
        """
        初始化客户端
        comm_type: 通信类型 'serial' 或 'tcp'
        port: 串口号 (仅serial)
        baudrate: 波特率 (仅serial)
        host: TCP服务器IP (仅tcp)
        port_tcp: TCP端口 (仅tcp)
        """
        self.comm_type = comm_type
        self.timeout = timeout
        self.crc_type = 'xmodem'
        self.connection = None
        
        if comm_type == 'serial':
            self.serial_port = port
            self.baudrate = baudrate
        elif comm_type == 'tcp':
            self.tcp_host = host
            self.tcp_port = port_tcp
        else:
            raise ValueError("通信类型必须是 'serial' 或 'tcp'")

    def connect(self):
        """建立连接"""
        try:
            if self.comm_type == 'serial':
                self.connection = serial.Serial(
                    port=self.serial_port,
                    baudrate=self.baudrate,
                    timeout=self.timeout
                )
                return self.connection.is_open
            else:  # TCP
                self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.connection.settimeout(self.timeout)
                self.connection.connect((self.tcp_host, self.tcp_port))
                return True
        except Exception as e:
            print(f"连接失败: {e}")
            self.connection = None
            return False

    def disconnect(self):
        """断开连接"""
        if self.connection:
            try:
                if self.comm_type == 'serial':
                    self.connection.close()
                else:
                    self.connection.close()
            except Exception as e:
                print(f"断开连接错误: {e}")
        self.connection = None

    def send_data(self, data):
        """发送数据"""
        if not self.connection:
            print("未建立连接")
            return False
        
        try:
            if self.comm_type == 'serial':
                self.connection.write((data + '\r\n').encode('utf-8'))
            else:
                self.connection.sendall((data + '\r\n').encode('utf-8'))
            return True
        except Exception as e:
            print(f"发送数据失败: {e}")
            return False

    def receive_data(self):
        """接收数据"""
        if not self.connection:
            print("未建立连接")
            return None
        
        try:
            if self.comm_type == 'serial':
                return self.connection.read_until(b'$end').decode('utf-8').strip()
            else:
                # TCP接收直到找到结束标记
                data = b''
                while True:
                    chunk = self.connection.recv(1024)
                    if not chunk:
                        return None
                    data += chunk
                    if b'$end' in data:
                        break
                return data.decode('utf-8').strip()
        except Exception as e:
            print(f"接收数据失败: {e}")
            return None

    def calculate_crc(self, data_str):
        """计算CRC16校验值"""
        crc16 = crcmod.predefined.Crc(self.crc_type)
        crc16.update(data_str.encode('utf-8'))
        return crc16.hexdigest().upper()

    def set_parameters(self, params):
        """
        设置参数
        params: 包含参数的字典
        返回: 响应字典或None
        """
        # 构建参数部分
        param_list = []
        for key, value in params.items():
            if key in ['fix_channels', 'fpga_gain']:
                param_list.append(f"{key}:[{';'.join(map(str, value))}]")
            else:
                param_list.append(f"{key}:{value}")
        
        param_str = ','.join(param_list)
        crc_value = self.calculate_crc(param_str)
        command = f"$set_parameters,{param_str},crc:{crc_value},$end"
        
        print(f"发送设置参数命令: {command}")
        if self.send_data(command):
            response = self.receive_data()
            if response and response.startswith('$set_parameters_callback'):
                print(f"收到响应: {response}")
                return self.parse_response(response)
            else:
                print("未收到有效响应")
        return None

    def get_parameters(self, params=None):
        """
        获取参数
        params: 要获取的参数列表，None表示获取所有参数
        返回: 响应字典或None
        """
        if params and isinstance(params, list) and len(params) > 0:
            param_str = ','.join(params)
            command = f"$get_parameters,{param_str},$end"
        else:
            command = "$get_parameters,$end"
        
        print(f"发送获取参数命令: {command}")
        if self.send_data(command):
            response = self.receive_data()
            if response and response.startswith('$parameters'):
                print(f"收到响应: {response}")
                return self.parse_response(response)
            else:
                print("未收到有效响应")
        return None

    def get_states(self):
        """
        获取设备状态
        返回: 状态字典或None
        """
        command = "$get_states,$end"
        print(f"发送获取状态命令: {command}")
        if self.send_data(command):
            response = self.receive_data()
            if response and response.startswith('$states'):
                print(f"收到响应: {response}")
                return self.parse_response(response)
            else:
                print("未收到有效响应")
        return None

    def get_versions(self):
        """
        获取版本信息
        返回: 版本字典或None
        """
        command = "$get_versions,$end"
        print(f"发送获取版本命令: {command}")
        if self.send_data(command):
            response = self.receive_data()
            if response and response.startswith('$versions'):
                print(f"收到响应: {response}")
                return self.parse_response(response)
            else:
                print("未收到有效响应")
        return None

    def parse_response(self, response):
        """解析响应字符串为字典"""
        if not response or '$end' not in response:
            return None
        
        # 提取响应主体
        start = response.find(',') + 1
        end = response.find(',$end')
        if start < 0 or end < 0 or start >= end:
            return None
        
        body = response[start:end]
        result = {}
        
        # 分割参数
        params = body.split(',')
        for param in params:
            if ':' in param:
                key, value = param.split(':', 1)
                
                # 处理数组类型参数
                if value.startswith('[') and value.endswith(']'):
                    array_value = value[1:-1].split(';')
                    # 尝试转换为数字
                    try:
                        result[key] = [int(x) for x in array_value]
                    except ValueError:
                        result[key] = array_value
                else:
                    # 尝试转换为数字
                    try:
                        result[key] = int(value)
                    except ValueError:
                        result[key] = value
        
        return result

if __name__ == "__main__":
    # 选择通信类型: 'serial' 或 'tcp'
    COMM_TYPE = 'serial'  # 或 'tcp'
    
    if COMM_TYPE == 'serial':
        # 串口配置
        client = ESP32CommandClient(
            comm_type='serial',
            port='COM9',  # 替换为实际串口号
            baudrate=115200
        )
    else:
        # TCP配置
        client = ESP32CommandClient(
            comm_type='tcp',
            host='192.168.0.66',  # ESP32的IP地址
            port_tcp=10123  # 端口号
        )
    
    # 连接设备
    if client.connect():
        try:
            # 1. 设置参数示例
            # print("\n=== 测试设置参数 ===")
            # test_params = {
            #     'rid_beacon': 1,
            #     'rid_bt4': 0,
            #     'rid_bt5': 1,
            #     'rid_2.4G': 1,
            #     'rid_5.8G': 0,
            #     'rid_work_mode': 1,
            #     'fix_channels': [1, 2, 4, 6, 147],
            #     'fpga_work_mode': 1,
            #     'fpga_check_type': '0F',
            #     'fpga_check_band': '07',
            #     'fpga_gain': [0, 0, 0, 0, 0, 56, 76, 76]
            # }
            # set_response = client.set_parameters(test_params)
            # if set_response:
            #     print("设置参数结果:", set_response)
            
            # time.sleep(2)
            
            # 2. 获取指定参数示例
            # print("\n=== 测试获取指定参数 ===")
            # get_spec_response = client.get_parameters(['rid_beacon', 'rid_work_mode', 'fix_channels'])
            # if get_spec_response:
            #     print("指定参数结果:", get_spec_response)
            
            # time.sleep(2)
            
            # 3. 获取所有参数示例
            # print("\n=== 测试获取所有参数 ===")
            # get_all_response = client.get_parameters()
            # if get_all_response:
            #     print("所有参数结果:", get_all_response)
            
            # time.sleep(2)
            
            # 4. 获取状态示例
            print("\n=== 测试获取状态 ===")
            states_response = client.get_states()
            if states_response:
                print("状态结果:", states_response)
            
            time.sleep(2)
            
            # 5. 获取版本示例
            # print("\n=== 测试获取版本 ===")
            # versions_response = client.get_versions()
            # if versions_response:
            #     print("版本结果:", versions_response)
            
        except KeyboardInterrupt:
            print("\n用户中断程序")
        finally:
            # 断开连接
            client.disconnect()
    else:
        print("无法建立连接，程序退出")
